clUARTVirt::clUARTVirt(void): clInterface(){
	_UBRRH	= 0;//0x01;//
	_UBRRL	= 0x33;//0xA0;//
	
#ifdef URSEL
	_UCSRC	= (1<<URSEL) + (1<<_UCSZ0) + (1<<_UCSZ1);
#else
	_UCSRC	= (1<<_UCSZ0) + (1<<_UCSZ1);
#endif
}

clRXBuffer::clRXBuffer(void): clMain(){
	currentItem		= 0;
	packakeLength	= 0;
	packageType		= packageNULL;
}

clTXBuffer::clTXBuffer(void): clMain(){
	Head			= 0;
	currentItem		= 0;
	packageStatus	= packageStatusNULL;
}

//void newprr(uint8_t b){}

uint8_t volatile clUARTVirt::rxTop					= 0;
uint8_t volatile clUARTVirt::txTop					= 0;
uint8_t volatile clUARTVirt::rxOccupiedBytes		= 0;
uint8_t volatile clUARTVirt::txFreeBytes			= MaxBufferLength;
uint8_t	volatile clUARTVirt::rxBuffer[MaxBufferLength];
uint8_t	volatile clUARTVirt::txBuffer[MaxBufferLength];
bool volatile clUARTVirt::rxOverflow				= false;
clTXBuffer volatile clUARTVirt::txPackage			= clTXBuffer();
clRXBuffer volatile clUARTVirt::rxPackage			= clRXBuffer();
const uint8_t clUARTVirt::defaultPackageSize[]		= {0, 1, 2, 4, 8, 16, 32, 64, 0, 0, 0, 0, 0};
void (*clUARTVirt::prr)(uint8_t)	= &(clUARTVirt::prr1);

bool clUARTVirt::CollectPackage(uint8_t source[], uint8_t *sourceHead, uint8_t packageType){
	bool result;
	
	uint8_t	destination[MaxBufferLength];

	uint8_t destinationHead = 0;
	uint8_t packageLength	= defaultPackageSize[packageType];
	
	destination[destinationHead]	= packageType;
	destinationHead++;
	result				= ArrayCopy(source, *sourceHead, destination, destinationHead, packageLength);
	SendPackage(destination);
	*sourceHead			+= packageLength;
	
	return result;
}


bool clUARTVirt::SendData(uint8_t *source, uint8_t sourcePackageLength){
	uint8_t sourceHead	= 0;
	uint8_t packageLength;
	bool result			= true;

	packageLength	= defaultPackageSize[packageData64];
	if  (sourcePackageLength / packageLength){
		result	= CollectPackage(source, &sourceHead, packageData64);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData32];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData32);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData16];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData16);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData8];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData8);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData4];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData4);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData2];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData2);
		sourcePackageLength	-= packageLength;
	}
	
	packageLength	= defaultPackageSize[packageData1];
	if  ((sourcePackageLength / packageLength) && result) {
		result	= CollectPackage(source, &sourceHead, packageData1);
		sourcePackageLength	-= packageLength;
	}
	return result;
}



bool clUARTVirt::SendPackage(uint8_t *lBytes){
	uint8_t Index	= 0;
	uint8_t lTXTop, lTXFreeBytes, packageType, packageLength;
	bool Success	= true;
	packageType		= lBytes[0];
	packageLength	= defaultPackageSize[packageType] + HeadLength;
	cli();
	
	lTXTop			= txTop;
	lTXFreeBytes	= txFreeBytes;

	while (Index < packageLength){
		if (!TXAddByte(&lBytes[Index])){
			txTop		= lTXTop;
			Success		= false;
			txFreeBytes	= lTXFreeBytes;
			break;
		}
		Index++;
	}
	
	FillTXPackage();
	sei();
	PinUp(_UCSRB, _UDRIE);
	return Success;
}

bool clUARTVirt::TXAddByte(uint8_t *lByte){
	if (txFreeBytes == 0) {return false;}
	
	txBuffer[txTop]	 = *lByte;

	txFreeBytes--;
	txTop++;
	txTop	= (txTop == MaxBufferLength)? 0: txTop;
	
	return true;
}

//void void (*prr)(void){};

void clUARTVirt::FillTXPackage(void){
	if (txPackage.packageStatus != packageStatusNULL || txFreeBytes == MaxBufferLength){return;}

	uint8_t byteIndex;
	
	txPackage.currentItem	= 0;
	byteIndex				= txTop - (MaxBufferLength - txFreeBytes);
	byteIndex				= (byteIndex < 0)? (MaxBufferLength - 1): byteIndex;

	uint8_t packageLength	= defaultPackageSize[txBuffer[byteIndex]] + HeadLength;
	while (packageLength--)
	{
		txPackage.Data[txPackage.Head]	= txBuffer[byteIndex];
		txFreeBytes++;
		txPackage.Head++;
		byteIndex++;
		byteIndex	= (byteIndex == MaxBufferLength)? 0: byteIndex;
	}
	
	txPackage.packageStatus = packageStatusSend;
}

void clUARTVirt::SendToUART(void){
	if ((!CheckPin(_UCSRA, _UDRE)) || (txPackage.Head < txPackage.currentItem)){
		return;
	}

	_UDR	= txPackage.Data[txPackage.currentItem];
	txPackage.currentItem++;
	
	if (txPackage.currentItem == txPackage.Head){
		txPackage.Head	= 0;
	}
}

void clUARTVirt::RXReplyOverflow(void){
	uint8_t Data = packageBufferOverflow;
	SendPackage(&Data);
	
	rxPackage.packageType	= packageNULL;
}

void clUARTVirt::RXReplySuccess(void){
	uint8_t Data = packageConfirm;
	SendPackage(&Data);

	rxPackage.packageType	= packageNULL;
}

void clUARTVirt::ByteToRXBuff(uint8_t lByte){
	rxBuffer[rxTop]	= lByte;
	rxTop++;
	
	rxTop	= (rxTop == MaxBufferLength)? 0: rxTop;
	rxOccupiedBytes++;
}

void clUARTVirt::prr1(uint8_t b){}


void clUARTVirt::FillRXBuff(void){
	if (rxPackage.packakeLength > (MaxBufferLength - rxOccupiedBytes)){
		RXReplyOverflow();
		rxOverflow	= true;
		return;
	}
	
	ByteToRXBuff(rxPackage.packakeLength);

	uint8_t byteCount	= rxPackage.packakeLength;
	byteCount--;
	uint8_t byteIndex	= 0;
	while (byteIndex < byteCount){
		ByteToRXBuff(rxPackage.Data[byteIndex]);
	}
}

void clUARTVirt::ReceiveFromUART(void){
	uint8_t lUDR	= _UDR;
	if (rxPackage.packageType == packageNULL){
		uint8_t pkType	= lUDR;
		switch (pkType){
			case packageConfirm:
				txPackage.Head	= 0;
				txPackage.packageStatus = packageStatusNULL;
				FillTXPackage();
				if (txPackage.Head == 0){
					PinDown(_UCSRB, (_UDRE));
				}
				return;
			case packageTimeOut:
			case packageError:
				txPackage.currentItem	= 0;
				return;
			case packageIncorrectLength:
			case packageBufferOverflow:
			case packageNULL:
				return;
			default:
				rxPackage.packageType	= (ePackageType)pkType;
				rxPackage.packakeLength	= defaultPackageSize[pkType];
				rxPackage.currentItem	= 0;
				
				if (rxPackage.packakeLength > MaxBufferLength){
					RXReplyOverflow();
				}
				return;
		}
	}
	
	rxPackage.Data[rxPackage.currentItem]	= lUDR;
	rxPackage.currentItem++;

	if (rxPackage.currentItem == rxPackage.packakeLength){
		FillRXBuff();
	}
}
/*
bool clUARTVirt::ReceiveByte(uint8_t lByte){
	if (rxOccupiedBytes == 0) {return false;}

	int8_t ByteIndex;
	
	ByteIndex	= rxTop - (MaxBufSize - rxOccupiedBytes);
	ByteIndex	= (ByteIndex < 0)? (MaxBufSize - 1): ByteIndex;

	*lByte		= rxBuff[ByteIndex];
	rxOccupiedBytes--;
	
	return true;
}
*/

bool clUARTVirt::GetRXPackage(uint8_t *lBuff, uint8_t *bufferSize){
	if (rxOccupiedBytes == 0) {return false;}

	int8_t rxBufferIndex, bufferIndex = 0;
	
	rxBufferIndex	= rxTop - (MaxBufferLength - rxOccupiedBytes);
	rxBufferIndex	= (rxBufferIndex < 0)? (MaxBufferLength - 1): rxBufferIndex;
	
	*bufferSize	= rxBuffer[rxBufferIndex];
	(*bufferSize)--;
	rxOccupiedBytes--;
	
	while (bufferIndex < *bufferSize){
		rxBufferIndex		= rxTop - (MaxBufferLength - rxOccupiedBytes);
		rxBufferIndex		= (rxBufferIndex < 0)? (MaxBufferLength - 1): rxBufferIndex;
		bufferIndex++;

		lBuff[bufferIndex]	= rxBuffer[rxBufferIndex];
	}
	
	rxOverflow	= false;
	return true;
}

clUART1::clUART1(void): clUARTVirt(){
	rxTop			= 0;
	txTop			= 0;
	rxOccupiedBytes	= 0;
	txFreeBytes		= MaxBufferLength;
}

void clUART1::On(void){
	sei();
	PinsUp(_UCSRB, (1<<_RXEN) + (1<<_TXEN) + (1<<_RXCIE));
}

void clUART1::Off(void){
	PinsDown(_UCSRB, (1<<_RXEN) + (1<<_TXEN) + (1<<_RXCIE));
}

void clUARTVirt::RXHandler(void){}

void clUARTVirt::UDREHandler(void){}

void clUART1::RXHandler(void){
	clUARTVirt::RXHandler();
	ReceiveFromUART();
}

void clUART1::UDREHandler(void){
	clUARTVirt::UDREHandler();
	SendToUART();
}

